package workflow.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import sysman.model.OrderDealHistory;

import workflow.dao.ProcessDao;
import workflow.model.ProcessDefinition;
import workflow.model.ProcessInstance;
import workflow.model.ProcessNode;
import workflow.model.ProcessVariable;
import workflow.util.ApplyRecordVo;


public class ProcessService {
	private ProcessDao processDao;
	
	public ProcessDao getProcessDao() {
		return processDao;
	}
	public void setProcessDao(ProcessDao processDao) {
		this.processDao = processDao;
	}
	/**
	 * 启动工单下发流程，引擎会把流程定义里的流程配置XML放到流程实例表中。 
	 * 如果在流程图中（流程定义）设置了变量，那么在variableMap中必须要有这个变量，
	 * 在用到这个变量之前赋值。每个流程实例必须有一个变量“owner”， 这个变量的值就是流程发起人ID。
	 * 
	 * @param processDefinitionId  ：流程定义ID
	 * @param variableMap          ：流程中设置的变量
	 * @param processInstanceName  ：流程实例的名称，可自行填写
	 * @param wb                   :工单，设置要下发的工单的属性
	 * @return task 返回一个TASK或NULL
	 */
	public ProcessNode startProcessInstanceById(String processDefinitionId, HashMap<String, String> variableMap, String processInstanceName) throws RuntimeException {
		ProcessNode task = null;
		try {
			ProcessDefinition pd = this.processDao.get(ProcessDefinition.class,
					Long.valueOf(processDefinitionId));

			ProcessInstance pi = new ProcessInstance();
			pi.setCreateDate(new Date());
			if (null != processInstanceName && !"".equals(processInstanceName)) {
				pi.setName(processInstanceName);
			} else {
				pi.setName(pd.getName());
			}
			pi.setProcessXml(pd.getProcessXml());
			pi.setProDefId(pd.getId());
			pi.setStatus(ProcessInstance.STATUS_EXEING);

			String owner = variableMap.get("owner");
			if (owner != null && !"".equals(owner)) {
				pi.setCreator(Long.valueOf(owner));
			}

			task = this.processDao.startProcessInstanceById(pi, variableMap);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		}

		return task;
	}
	
	/**
	 * 关闭任务，并且保存表单变量；
	 * 流程根据转向跳转到下一节点；
	 * 如果本节点有多个转向，则选择第一个转向跳转；
	 * 如果没有转向，则抛出异常；
	 * 
	 * @param taskId        流程节点ID
	 * @param variableMap   变量表，设置流程用到的变量的值
	 * @param wb            工单信息
	 * @throws RuntimeException
	 */ 
	public void completeTask(String taskId, HashMap<String, String> variableMap) throws RuntimeException{
		this.completeTask(taskId, variableMap, null);
	}
	/**
	 * 用于审批流程
	 * 
	 * 关闭任务，并且保存表单变量；
	 * 流程根据转向跳转到下一节点；
	 * 根据outcome转向；可设置NULL;
	 * 如果本节点有多个转向，则选择第一个转向跳转；
	 * 如果没有转向，则抛出异常；
	 * 
	 * @param taskId        流程节点ID
	 * @param variableMap   变量表，设置流程用到的变量的值
	 * @param outcome       转向名称，流程将会转到这个转向
	 * @throws RuntimeException
	 */
	public void completeTask4Apply(String taskId, HashMap<String, String> variableMap, String outcome) throws RuntimeException{
		ProcessNode rTask = null;
		try {
			ProcessNode task = this.processDao.findUniqueBy(ProcessNode.class, "id", Long
					.valueOf(taskId));
			task.setStatus("2");
			task.setDueDate(new Date());

			List<ProcessVariable> list = new ArrayList<ProcessVariable>();
			Set keys = variableMap.keySet();
			Iterator<String> it = keys.iterator();
			String key = null;
			String value = null;
			ProcessVariable var = null;

			while (it.hasNext()) {
				key = it.next();
				value = variableMap.get(key);

				var = new ProcessVariable();
				var.setProInsId(task.getProInsId());
				var.setVarName(key);
				var.setVarValue(value);
				var.setNodeId(task.getId());
				var.setVersion(task.getVersion());

				list.add(var);
			}

			this.processDao.completeTask(task, list, outcome);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		}
	}
	
	
	/**
	 *  关闭任务，并且保存表单变量；
	 * 流程根据转向跳转到下一节点；
	 * 根据outcome转向；
	 * 如果找不到指定的转向，则抛出异常；
	 * 
	 * @param taskId        流程节点ID
	 * @param variableMap   变量表，设置流程用到的变量的值
	 * @param outcome       转向名称，流程将会转到这个转向
	 * @param wb            工单信息
	 * @throws RuntimeException
	 */
	public void completeTask(String taskId, HashMap<String, String> variableMap, String outcome) throws RuntimeException {
		ProcessNode rTask = null;
		try {
			ProcessNode task = this.processDao.findUniqueBy(ProcessNode.class, "id", Long
					.valueOf(taskId));
			task.setStatus("2");
			task.setDueDate(new Date());

			List<ProcessVariable> list = new ArrayList<ProcessVariable>();
			Set keys = variableMap.keySet();
			Iterator<String> it = keys.iterator();
			String key = null;
			String value = null;
			ProcessVariable var = null;

			while (it.hasNext()) {
				key = it.next();
				value = variableMap.get(key);

				var = new ProcessVariable();
				var.setProInsId(task.getProInsId());
				var.setVarName(key);
				var.setVarValue(value);
				var.setNodeId(task.getId());
				var.setVersion(task.getVersion());

				list.add(var);
			}

			this.processDao.completeTask(task, list, outcome);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		}
	}
	
	/**
	 * 关闭任务，并且根据outcome转向；
	 * 如果找不到指定的转向，则抛出异常；
	 * 
	 * @param taskId        流程节点ID
	 * @param outcome       转向名称，流程将会转到这个转向
	 * @param wb            工单信息
	 * @throws RuntimeException
	 */
	public void completeTask(String taskId, String outcome) throws RuntimeException{
		HashMap<String, String> variableMap = new HashMap<String, String>();
		this.completeTask(taskId, variableMap, outcome);
	}
	
	/**
	 * 关闭任务;
	 * 流程根据转向跳转到下一节点；
	 * 如果本节点有多个转向，则选择第一个转向跳转；
	 * 如果没有转向，则抛出异常；
	 * 
	 * @param taskId
	 * @param wb
	 */
	public void completeTask(String taskId) throws RuntimeException{
		HashMap<String, String> variableMap = new HashMap<String, String>();
		this.completeTask(taskId, variableMap, null);
	}
	
	/**
	 * 查找指定用户的任务
	 * 
	 * @param userId
	 * @return
	 */
	public List<ProcessNode> findPersonalTasks(String userId){
		return this.processDao.findBy(ProcessNode.class, "assignee", Long.valueOf(userId));
	}
	
	
	/**
	 * 设置本节点的变量
	 * 
	 * @param taskId
	 * @param variables key : 变量名， value : 变量值 (例如：key=标段标识 value=0不需要，=1需要)
	 * @throws RuntimeException 
	 */
	public void setVariables(String taskId, HashMap<String, String> variables) throws RuntimeException{
		Long lTaskId = Long.valueOf(taskId);
		ProcessNode task = this.processDao.findUniqueBy(ProcessNode.class, "id", lTaskId);
		List<ProcessVariable> list = new ArrayList<ProcessVariable>();
		Set keys = variables.keySet();
		Iterator<String> it = keys.iterator();
		String key = null;
		String value = null;
		ProcessVariable var = null;
		
		while(it.hasNext()){
			key = it.next();
			value = variables.get(key);
			
			var = new ProcessVariable();
			var.setProInsId(task.getProInsId());
			var.setVarName(key);
			var.setVarValue(value);
			var.setNodeId(lTaskId);
			var.setVersion(1);
			
			list.add(var);
		}
		
		String hql = "FROM ProcessVariable WHERE proInsId = " + task.getProInsId();
		List<ProcessVariable> pvList = this.processDao.find(hql);
		
		String varName = null;
		boolean isSameVar = false;
		for(ProcessVariable pv : list){
			isSameVar = false;
			varName = pv.getVarName();
			for(ProcessVariable po : pvList){
				if(varName.equals(po.getVarName())){
					po.setVarValue(pv.getVarValue());
					isSameVar = true;
					break;
				}
			}
			
			if(!isSameVar){
				pvList.add(pv);
			}
		}
		
		try{
			this.processDao.saveOrUpdateAll(pvList);
		} catch (Exception e){
			e.printStackTrace();
			throw new RuntimeException("保存变量出错。");
		}
	}
	

	/**
	 * 返回以逗号相隔的工单类型变量
	 * 
	 * @param taskId
	 * @return
	 */
	public String getBillTypes(Long taskId){
		StringBuilder sb = new StringBuilder();
		String billTypes = "";
		ProcessNode task = this.processDao.findUniqueBy(ProcessNode.class, "id", taskId);
		List<ProcessNode> list = this.processDao
				.find("FROM " + ProcessNode.class.getName() + " WHERE nodeType ='decision' AND proInsId = "
						+ task.getProInsId());
		String exp = null;
		String[] arr = null;
		for(ProcessNode po : list){
			exp = po.getExpression();
			if(exp != null && exp.startsWith("#")){
				exp = exp.replace("#", "");
				exp = exp.replace("{", "");
				exp = exp.replace("}", "");
				arr = exp.split("==");
				if(arr != null && arr.length == 2){
					exp = arr[0].trim();
					sb.append(exp).append(",");
				}
			}
		}
		
		billTypes = sb.toString();
		if(billTypes.length() > 1){
			billTypes = billTypes.substring(0, billTypes.length() - 1);
		}
		
		return billTypes;
	}
	
	/**
	 * 获取流程定义的任务节点
	 * 
	 * @param pdfId 流程定义ID
	 * @return
	 */
	public String[] getTasksByProcessDefinitionId(Long pdfId){
		String[] names = null;
		ProcessDefinition pd = this.processDao.findUniqueBy(ProcessDefinition.class, "id", pdfId);
		String xml = pd.getProcessXml();
		try {
			Document document = DocumentHelper.parseText(xml);
			Element root = document.getRootElement();
			List list = root.selectNodes("//task-node");
			names = new String[list.size()];
			
			Element el = null;
			for(int i = 0; i < list.size(); i++){
				el = (Element)list.get(i);
				names[i] = el.attributeValue("name");
			}
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		
		return names;
	}
	
	/**
	 * 获取流程中的百分比节点
	 * 
	 * @param taskId
	 * @return
	 */
	public List<ProcessNode> getPercentPoints(Long taskId) {
		ProcessNode task = this.processDao.findUniqueBy(ProcessNode.class, "id", taskId);
		List<ProcessNode> list = this.processDao.find("FROM " + ProcessNode.class.getName() + " WHERE proInsId = "
				+ task.getProInsId() + " AND percentPoint is not null ORDER BY percentPoint");

		return list;
	}
	
	/**
	 * 通过节点的ID,查找指定的变量，含变量名和值
	 * 
	 * @param taskId
	 * @param variableNames
	 * @return
	 */
	public Map<String, String> getVariables(String taskId, Set<String> variableNames){	
		HashMap<String, String> map = new HashMap<String, String>();
		if(variableNames == null || variableNames.size() < 1) return map;
		
		
		String varNames = "";
		Iterator it = variableNames.iterator();
		
		while(it.hasNext()){			
			varNames += "'" + it.next() + "',";
		}
			
		if(varNames.length() > 2){
			varNames = varNames.substring(0, varNames.length() - 1);
		} else {
			return map;
		}
		
		ProcessNode task = this.processDao.findUniqueBy(ProcessNode.class, "id", Long.valueOf(taskId));
		String hql = "FROM ProcessVariable WHERE proInsId=" + task.getProInsId() + " AND varName IN(" + varNames + ")";
		List<ProcessVariable> list = this.processDao.find(hql);
		

		for(ProcessVariable po : list){
			map.put(po.getVarName(), po.getVarValue());
		}
		
		return map;
	}
	
	/**
	 * 根据流程节点ID查找审批记录
	 * 
	 * @param taskId
	 * @return
	 */
	public List<ApplyRecordVo> getApplyRecordByTaskId(Long taskId){
		List<ApplyRecordVo> arvList = new ArrayList<ApplyRecordVo>();
		ApplyRecordVo arv;
		List<OrderDealHistory> arList;
		OrderDealHistory ar;
		
		ProcessNode task = this.processDao.findUniqueBy(ProcessNode.class, "id", taskId);
		List<ProcessNode> taskList = this.processDao.find("FROM " + ProcessNode.class.getName() + " where proInsId = " + task.getProInsId() + " order by seq");
		
		
		for(ProcessNode tk : taskList){
			arList = this.processDao.findBy(OrderDealHistory.class, "taskId", tk.getId());
			arv = new ApplyRecordVo();
			arvList.add(arv);
			
			if(arList != null && arList.size() > 0){
				ar = arList.get(0);
				
				arv.setApplyCreateTime(ar.getCreateTime());
				arv.setApplyCreator(ar.getCreator());
				arv.setApplyCreatorName(ar.getCreatorName());
				arv.setApplyId(ar.getId());
				arv.setApplyNewScore(ar.getNewScore());
				arv.setApplyOldScore(ar.getOldScore());
				arv.setApplyReason(ar.getReason());
				arv.setApplyResult(ar.getResult());
				arv.setApplyTaskId(ar.getTaskId());
				arv.setApplyTaskName(tk.getName());
				arv.setSeq(tk.getSeq());
				arv.setApplyStauts("已审批");
			} else {
				arv.setSeq(tk.getSeq());
				arv.setApplyStauts("未审批");
				arv.setApplyTaskId(tk.getId());
				arv.setApplyTaskName(tk.getName());
			}
			
			//开始和结束节点，特殊处理
			if(tk.getName().indexOf("开始") != -1){
				arv.setApplyTaskName("<font color=blue>开始</font>");
				arv.setApplyStauts("");
			}
			
			if(tk.getName().indexOf("结束") != -1){
				arv.setApplyTaskName("<font color=blue>结束</font>");
				arv.setApplyStauts("");
			}
		}
		
		return arvList;
	}
	

}
